{***************************************************************
    SpyGestion  Copyright (C) 2008 Christophe KOALI <nature2gas@gmail.com>
    
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
****************************************************************}
unit modUnitAndDefInfos;

interface

uses modBaseType, Classes, SysUtils, sFonctionsChaines;



type
   TUnitInfos = record
       // Id de l'unité
       GID: integer;

       // pour l'estimation des ruines
       Prix: TOGRessourceAll;

       // Normalement la somme du metal et du cristal de Prix
       PointsStructure: integer;

       // Points d'attaque
       Attaque: integer;
       Bouclier: integer;

       // Fret/Vitesse/Consommation (0 si c'est une unité de défense)
       Fret: integer;
       Vitesse: integer;
       Consommation: integer;

       
       // Nb de points pour évaluer correctement la défense
       // ou la puissance d'une flotte
       ScoreDef: integer;
   end;

(*
Points de structure  	60000
Puissance du bouclier 	200
Valeur d'attaque 	1000
Capacité de fret 	1500 Unités
Vitesse de base 	10000
Consommation de carburant (Deutérium) 	500
*)


    PUnitInfos = ^TUnitInfos;
    TCollectionUnitInfos = class(TList)
    private
       {--------------------------------------------------}
       {Elements spécialisés de la classes}
       FFichier: string;

      
       {--------------------------------------------------}
       {Gestion des pointeurs}
       // Utilisé pour la propriété Itemx
       function GetItemx(Index: integer): TUnitInfos;
    public
       property FichierSave: string read FFichier write FFichier;
       
       
       {--------------------------------------------------}
       {Gestion des pointeurs}
       // *** Ajout de pointeurs
       // Renvoi le pointeur de l'élément ajouté
       function Add(Element: TUnitInfos): PUnitInfos;
      
       // Simple ajout de pointeur
       procedure SimpleAdd(Pointeur: PUnitInfos);
      
       // Renvoi le pointeur de l'élément ajouté/modifié
       // Utilise Get_Index pour vérifier si l'élément doit être mis à jour
       function Add_Update(Element: TUnitInfos): PUnitInfos;
      
      
       // *** Suppression de pointeur
       // Libère et supprime le pointeur Index
       // Renvoi True si réussit, False si hors limite ou erreur
       function Delete(Index: integer): boolean; 

      
       // *** Effacement de la liste de pointeur
       procedure Clear; override;
      
      
       // *** Requete sur la liste de pointeur
       function Get_Index(Element: integer): integer;
       function Get_Pointeur(Element: integer): PUnitInfos;
       function Get_Item(Index: integer): TUnitInfos;
       property Itemx[Index: integer]: TUnitInfos read GetItemx;


       // Renvoi le cout de n'importe quoi
       function GetCout(gid: integer; Niveau: integer): TOGRessourceAll;


       {--------------------------------------------------}
       {Procédures et fonctions spécialisées de la classes}
       // Destructeur
       destructor Destroy; override;
       constructor Create;
       
       //function SaveToFile(Fichier: string): integer;
       function LoadFromFile(Fichier: string): integer;
       //function LoadFromFileGetNextValeur(var ChaineLu: string): string;
    end;



implementation

uses modGidToStr, Math;

function mbtGetCoutGIDFacteur(Niveau1: TOGRessourceAll; Niveau: integer; Facteur: double): TOGRessourceAll;
var r: double;
    niv: double;
begin
    // coût au niveau n = (coût au niveau 1) * 1,5^(n-1)

    Dec(Niveau);
    niv := Niveau;

    r := power(Facteur, niv);

    Result.Metal     := round(Niveau1.Metal     * r);
    Result.Cristal   := round(Niveau1.Cristal   * r);
    Result.Deuterium := round(Niveau1.Deuterium * r);
end;

function TCollectionUnitInfos.GetCout(gid: integer; Niveau: integer): TOGRessourceAll;
var bNotFound: boolean;
    iTypeGID: TTypeGID;
    pe: PUnitInfos;
begin

    Result.Metal     := 0;
    Result.Cristal   := 0;
    Result.Deuterium := 0;
    Result.Energie   := 0;

    if Niveau <= 0 then begin
       exit;
    end;

    pe := Get_Pointeur(gid);

    if pe = nil then begin
       // meme pas la peine de continuer...
       exit;
    end;

    bNotFound := true;



    case gid of
        CNT_BATIMENT_METAL: begin

           if Niveau > 1 then begin
              // coût au niveau n = (coût au niveau 1) * 1,5^(n-1)
              Result := mbtGetCoutGIDFacteur(pe.Prix, Niveau, 1.5);
           end else begin
              Result := pe.Prix;
           end;
           bNotFound := false;
        end;


        CNT_BATIMENT_CRISTAL: begin
           // coût au niveau n = (coût au niveau 1) * 1,6^(n-1)

           if Niveau > 1 then begin
              Result := mbtGetCoutGIDFacteur(pe.Prix, Niveau, 1.6);
           end else begin
              Result := pe.Prix;
           end;
           bNotFound := false;
        end;


        CNT_BATIMENT_DEUT: begin
           // coût au niveau n = (coût au niveau 1) * 1,5^(n-1)
           
           if Niveau > 1 then begin
              Result := mbtGetCoutGIDFacteur(pe.Prix, Niveau, 1.5);
           end else begin
              Result := pe.Prix;
           end;
           bNotFound := false;
        end;

        CNT_BATIMENT_CENTRALE_SOLAIRE: begin
           // coût au niveau n = (coût au niveau 1) * 1,5^(n-1)

           if Niveau > 1 then begin
              Result := mbtGetCoutGIDFacteur(pe.Prix, Niveau, 1.5);
           end else begin
              Result := pe.Prix;
           end;
           bNotFound := false;
        end;

        CNT_BATIMENT_CENTRALE_FUSION: begin
           // coût au niveau n = (coût au niveau 1) * 1,8^(n-1)

           if Niveau > 1 then begin
              Result := mbtGetCoutGIDFacteur(pe.Prix, Niveau, 1.8);
           end else begin
              Result := pe.Prix;
           end;
           bNotFound := false;
        end;

    end;


    if bNotFound then begin

        if (Niveau > 1) then begin
           iTypeGID := GetTypeOfTGID(gid);

           case iTypeGID of
              tgidBatiment : mbtGetCoutGIDFacteur(Result, Niveau, 2.0);
              tgidRecherche: mbtGetCoutGIDFacteur(Result, Niveau, 2.0);
              tgidVaisseau: Result := pe.Prix;
              tgidDefense:  Result := pe.Prix;
           end;

        end else begin
           Result := pe.Prix;
        end;

    end;

end;






{*******************************************************************************
 ******************************************************************************* 
 **               TCollectionUnitInfos                                     **
 *******************************************************************************
 *******************************************************************************}
 

(*
// Lorsque l'on sauvegarde un token du genre t1;t2;t3
// Cette fonction renvoi le token suivant 
// Attention: si deux délimiteurs se suivent, une chaine vide sera renvoyée)
function TCollectionUnitInfos.LoadFromFileGetNextValeur(var ChaineLu: string): string;
var xPos: integer;
begin
     Result := '';

     xPos := pos(',', ChaineLu);

     if xPos = 0 then begin
        Result := ChaineLu;
        ChaineLu := '';
        exit;
     end;

     Result := copy(ChaineLu, 1, xPos - 1);
     System.Delete(ChaineLu, 1, xPos);
end;
*)

procedure Init(var e: TUnitInfos);
begin
   e.GID := -1;
   e.Prix.Metal := 0;
   e.Prix.Cristal := 0;
   e.Prix.Deuterium := 0;
   e.Prix.Energie := 0;

   e.PointsStructure := 0;
   e.Attaque := 0;
   e.Bouclier := 0;

   e.Fret := 0;
   e.Vitesse := 0;
   e.Consommation := 0;

   e.ScoreDef := 1;

end;


function LoadFromLigne(var e: TUnitInfos; Key: string; Valeur: integer): boolean;
begin

    Result := true;

    if SameText(Key, 'Metal') then begin
       e.Prix.Metal := Valeur;

    end else if SameText(Key, 'Cristal') then begin
       e.Prix.Cristal := Valeur;

    end else if SameText(Key, 'Deuterium') then begin
       e.Prix.Deuterium := Valeur;

    end else if SameText(Key, 'Energie') then begin
       e.Prix.Energie := Valeur;

    end else if SameText(Key, 'Structure') then begin
       e.PointsStructure := Valeur;

    end else if SameText(Key, 'Attaque') then begin
       e.Attaque := Valeur;

    end else if SameText(Key, 'Bouclier') then begin
       e.Bouclier := Valeur;

    end else if SameText(Key, 'Fret') then begin
       e.Fret := Valeur;

    end else if SameText(Key, 'Vitesse') then begin
       e.Vitesse := Valeur;

    end else if SameText(Key, 'Consommation') then begin
       e.Consommation := Valeur;

    end else if SameText(Key, 'ScoreDef') then begin
       e.ScoreDef := Valeur;

    end else begin
       Result := false;
    end;


end;


function TCollectionUnitInfos.LoadFromFile(Fichier: string): integer;
const CNTERREUR = -999;
var Element: TUnitInfos;
    F: TextFile;
    Ligne: string;
    xPos: integer;
    Valeur: integer;
    Key: string;
begin


    if Fichier = '' then Fichier := FFIchier;

    // ATTENTION: Les éléments sont effacés avant le chargement, à modifier en fonction de ce que l'on veut
    Clear;
    
    Result  := 0;
    Fichier := sfcUnixPathToDosPath(Fichier);
    FFichier := Fichier;
    if FileExists(Fichier) = false then exit;
    
    
    AssignFile(F, FFichier);
    
    
    {$I-}
    ReSet(F);
    {$I+}

    // IOResult renvoie 0 => cela signifie que la dernière opération d'E/S a réussi
    if NoT(IOResult = 0) then begin
       Result := -1;
       exit;
    end;

    Init(Element);

    While NoT(EoF(F)) do begin
         // Lit une ligne
         ReadLn(F, Ligne);

         Ligne := trim(ligne);
         if ligne = '' then Continue;
         if ligne[1] = '/' then Continue;
         if ligne[1] = ';' then Continue;
         if ligne[1] = '#' then Continue;
         if ligne[1] = '@' then Continue;


         if Ligne[1] = '[' then begin

            if Element.GID > 0 then begin

               if Element.PointsStructure <= 1 then begin
                  Element.PointsStructure := Element.Prix.Metal + Element.Prix.Cristal;
               end;

               Add(Element);
            end;

            Init(Element);

            System.Delete(Ligne, 1, 1);
            xPos := pos(']', Ligne);

            if xPos = 0 then Continue;

            System.Delete(Ligne, xPos, maxint);

            Element.GID := StrToIntDef(Ligne, -1);
            Continue;
         end;

         Valeur := sfcTokenGetInt(Ligne, 2, '=', CNTERREUR);
         if Valeur = CNTERREUR then
            Continue;

         Key := sfcTokenGet(Ligne, 1, '=');

         LoadFromLigne(Element, Key, Valeur);

    end;

    if Element.GID > 0 then begin
       if Element.PointsStructure <= 1 then begin
          Element.PointsStructure := Element.Prix.Metal + Element.Prix.Cristal;
          end;

          Add(Element);
    end;

    // Fermeture du fichier
    CloseFile(F);


    Result := Count;

end;



(*
function TCollectionUnitInfos.SaveToFile(Fichier: string): integer;
var i: integer;
    F: TextFile;
    pElement: PUnitInfos;
begin

    Result := -2;

    if Fichier = '' then Fichier := FFichier;
    
    AssignFile(F, Fichier);
    
    {$I-}
    ReWrite(F);
    {$I+}
    
    // IOResult renvoie 0 => cela signifie que la dernière opération d'E/S a réussi
    if NoT(IOResult = 0) then begin
       Result := -1;
       exit;
    end;
    
    for i := 0 to Count - 1 do begin
        pElement := Items[i];
        
        //
        // Ecriture ici
        //
    end;
    
    // Résultat
    Result := Count;
    
    // Ferme le fichier
    CloseFile(F);
    
end;
*)



constructor TCollectionUnitInfos.Create;
begin
    
    // Héritage
    InHerited Create;
    
    
    
    // Code pour l'initialisation ici
    
    
end;

destructor TCollectionUnitInfos.Destroy;
begin
    // Implémenter le code avant l'héritage de la méthode
    // C'est à dire ici
    
   
    // On appelle NOTRE clear qui lui-même appelle NOTRE Delete qui libère l'espace mémoire
    // avant d'enlever le pointeur de la TList
    // L'héritage de Destroy appelle Clear, mais es-ce le notre ou celui de la classe de base ?
    Clear;
    
    
    // Héritage
    InHerited Destroy;
end;


procedure TCollectionUnitInfos.Clear;
var i: integer;
begin

    if Count > 0 then begin
        For i := Count -1 downto 0 do
            Delete(i);
    end;
    
    inherited Clear;
end;


function TCollectionUnitInfos.Get_Pointeur(Element: integer): PUnitInfos;
var i: integer;
begin

   Result := nil;
   
   i := Get_Index(Element);
   if i < 0 then exit;
   Result := Items[i];

end;


function TCollectionUnitInfos.Get_Index(Element: integer): integer;
var i: integer;
    pElement: PUnitInfos;
begin

   Result := -1;

   if Count <= 0 then exit;

   for i := 0 to Count - 1 do begin
      pElement := Items[i];

      {### ### ### ### ### ### ### ### ### ### ### ### ### ### }
      {### Neccessite adpation en fonction du type record  ### }
      {### ### ### ### ### ### ### ### ### ### ### ### ### ### }

      if pElement^.GID = Element then begin
         Result := i;
         exit;
      end;
   end;

end;



function TCollectionUnitInfos.Add_Update(Element: TUnitInfos): PUnitInfos;
var i : integer;
begin
     
     Result := nil;
                                         {### ### ### ### ### ### ### ### ### ### ### ### ### ### }
     i := Get_Index(Element.GID);        {### Neccessite adpation en fonction du type record  ### }
                                         {### ### ### ### ### ### ### ### ### ### ### ### ### ### }
     if i < 0 then begin 
        // L'élément n'existe pas, l'ajoute
        Result  := Add(Element);
     end else begin
        // L'élément existe déjà, le met à jour
        Result  := Items[i];     // Obtient le pointeur
        Result^ := Element;
     end;
     
end;




function TCollectionUnitInfos.Get_Item(Index: integer): TUnitInfos;
var pElement: PUnitInfos;
begin

     if Index < 0 then exit;
     if Index >= Count then exit;

     pElement := Items[Index];     // Obtient le pointeur
     Result := pElement^;

end;

function TCollectionUnitInfos.Delete(Index: integer): boolean;
var pElement: PUnitInfos;
begin

    Result := false;

    if (Index < 0) or (Index >= Count) then
       // Hors limite
       exit;


    // Obtient le pointeur
    pElement := Items[Index];

    if pElement <> nil then
       // Libére la mémoire créée lors de l'ajout
       Dispose(pElement);

    // Supprime le pointeur de la liste
    inherited Delete(Index);

    // Met le resultat à True
    Result := true;

end;



function TCollectionUnitInfos.Add(Element: TUnitInfos): PUnitInfos;
var pElement: PUnitInfos;
begin
    
    Result := nil;
    
    New(pElement);          // Créer l'espace mémoire requis
    pElement^ := Element;   // Recopie les données dans l'emplacement pointé
    
    try
       inherited Add(pElement);  // Ajoute le pointeur à la liste
       Result := pElement;
    except
       Result := nil;
    end;
    
    // Pour libérer la mémoire occupé par l'élément pointer:  Dispose(pElement);

end;

// Simple ajout de pointeur
procedure TCollectionUnitInfos.SimpleAdd(Pointeur: PUnitInfos);
begin
    
    inherited Add(Pointeur);  // Ajoute le pointeur à la liste
    
end;

function TCollectionUnitInfos.GetItemx(Index: integer): TUnitInfos;
var pElement: PUnitInfos;
begin

   if Count < 0 then exit;
   if Index >= Count then exit;

   pElement := Items[Index];
   Result := pElement^;

end;


end.
